/** @file if_sdio.c
  *  
  * @brief This file contains SDIO related functions
  * in Bluetooth driver.
  * 
  * (c) Copyright 2003-2007, Marvell International Ltd.
  * All Rights Reserved
  */

#ifdef IF_SDIO

#include <memory.h>
#include <SDCardDDK.h>

#include "Firmware.h"
#include "bt_def.h"
#include "bt_dev.h"
#include "bt_func.h"
#include "sdio.h"
#include "igxBug.h"


/********************************************************
        Local Variables
********************************************************/
// structure used for fw download is a little different
// from regular packet download
struct _SDIO_FW_DWLD_PKT
{    
    ULONG  Len;    
    UCHAR  Buf[SDIO_FW_DOWNLOAD_BLOCK_SIZE];
};

/********************************************************
        Global Variables
********************************************************/

/********************************************************
        Local Functions
********************************************************/

/********************************************************
        Global Functions
********************************************************/

IF_FW_STATUS sdio_IsFirmwareLoaded(PMRVDRV_ADAPTER Adapter)
{
    USHORT usLength;

    // read scratch register for FW init code, FN1
    if (sdio_ReadScratch(Adapter, FW_DNLD_FUNC, &usLength) != IF_SUCCESS)
    {
        DBGPRINT(DBG_LOAD|DBG_WARNING,
                 (L"sdio_IsFirmwareLoaded: Unable to read FW init code\n\r"));
        return FW_STATUS_READ_FAILED;
    }

    DBGPRINT(DBG_LOAD, (L"Read Scratch : 0x%04x\n\r", usLength));

    if (usLength == SDIO_FW_INIT_CODE)
    {
        // Code Expected 
        DBGPRINT(DBG_LOAD|DBG_WARNING, (L"Firmware is INITIALIZED !\n\r"));
        return FW_STATUS_INITIALIZED;
    }

    DBGPRINT(DBG_LOAD|DBG_WARNING, (L"Firmware is UNINITIALIZED !\n\r"));

    return FW_STATUS_UNINITIALIZED;
}


IF_FW_STATUS sdio_FirmwareDownload(PMRVDRV_ADAPTER Adapter)
{
    struct _SDIO_FW_DWLD_PKT    DownloadPkt;
    UINT    blockCount = 0;
    UINT    sizeOfFirmware, sizeOfHelper;
    UINT    sizeSend = 0;
    USHORT  sizeBlock; 
    ULONG   firmwareDelay = 0;
    SD_API_STATUS               status;          // intermediate status
    SD_TRANSFER_CLASS           transferClass;   // general transfer class
    SD_COMMAND_RESPONSE         response;        // IO response status
    DWORD                       argument;        // argument
    ULONG                       numBlocks;       // number of blocks
    UCHAR                       ucCardStatus;
    UCHAR                       ucLen[2];
    ULONG                       loopCount;
    BOOLEAN                     startFirmware;   // send down firmware start
                                                 // pkt in the next round
    BOOLEAN                     exitLoop;
#ifdef EXTERNAL_FW_IMAGE
    UCHAR                       name[20];
    ULONG                       flen;
    PUCHAR                      vaddr;
#endif

    //
    // Set up NIC HW (include firmwware download)
    //

#ifdef MRVL_WINCE50
    UCHAR   regValue;

    regValue = 3;
    // Enable Client UpLdCardRdy Interrupt
    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_WRITE,          
                                        FW_DNLD_FUNC,     
                                        HCR_HOST_INT_MASK_REGISTER,
                                        FALSE,
                                        &regValue,
                                        1);   
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ERROR,
                 (L"Failed to enable UpLdCardRdy interrupt: 0x%08X\n\r",
                 status));
        return FW_STATUS_UNINITIALIZED;
    } 
#endif //MRVL_WINCE50 

#ifdef EXTERNAL_FW_IMAGE
    strcpy((PUCHAR)&name[0], "helper.bin");  
    //Read in helper image    
    if ((status = Fopr_ReadImage(Adapter, (PUCHAR)&name[0], &flen, &vaddr)) 
                  != MRVL_STATUS_SUCCESS)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to READ the firmware\n\r")); 
        return FW_STATUS_READ_FAILED; 
    }    
    sizeOfHelper = flen;
#else
    //Download helper file
    sizeOfHelper = sizeof(helperimage);
#endif

    DBGPRINT(DBG_LOAD|DBG_HELP,
             (L"INIT - Helper, Helper size = %d bytes, block size=%d bytes\n\r", 
             sizeOfHelper, SDIO_FW_DOWNLOAD_BLOCK_SIZE));

    startFirmware = FALSE;
    exitLoop = FALSE;
    while (!exitLoop)
    {
        blockCount++;

        if (TRUE != startFirmware)
        {
            // regular download
            sizeBlock = SDIO_FW_DOWNLOAD_BLOCK_SIZE;

            // last pkt is not necessarily full pkt
            if ((sizeSend + sizeBlock) >= sizeOfHelper)
            {
                sizeBlock = sizeOfHelper - sizeSend;
                startFirmware = TRUE;
            }
#ifdef EXTERNAL_FW_IMAGE
            MrvlMoveMemory(DownloadPkt.Buf, (PUCHAR)(vaddr+sizeSend), sizeBlock);
#else
            // Move the data from firmware buf to the command header
            MrvlMoveMemory(DownloadPkt.Buf, &helperimage[sizeSend], sizeBlock);
#endif
        }
        else
        {
            // download pkt of size 0 to kick start firmware
            sizeBlock = 0;

            // exit loop at the complete of the current loop
            exitLoop = TRUE;
        }

        DownloadPkt.Len = sizeBlock;
        
        // this is almost like downloading regular pkt

        loopCount = PKT_WAIT_TIME;

        while (loopCount != 0)
        {
            // need to check if FW is ready for download

            // read card status register (function 1,addr 20)
            status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                                SD_IO_READ,
                                                FW_DNLD_FUNC,
                                                HCR_HOST_CARD_STATUS_REGISTER,
                                                FALSE,
                                                &ucCardStatus,
                                                sizeof(ucCardStatus));
            if (!SD_API_SUCCESS(status))
            {       
#ifdef EXTERNAL_FW_IMAGE
                Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#endif
                return FW_STATUS_READ_FAILED; 
            }

            if ((!(ucCardStatus & SDIO_IO_READY)) ||  
                (!(ucCardStatus & SDIO_DOWNLOAD_CARD_READY)))
            {
                // Not ready for packet download
        
                loopCount--;
                Sleep(1);
            }
            else
            {
                break;
            }
        }

        if (loopCount == 0)
        {
            DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Downloading FW died on block %d\n\r", blockCount));
            return FW_STATUS_UNINITIALIZED; 
        }

        // Len field ONLY accounts for the buffer size
        numBlocks = (DownloadPkt.Len + sizeof(ULONG)) / 
                            SDIO_EXTENDED_IO_BLOCK_SIZE;

        if (((DownloadPkt.Len + sizeof(ULONG)) % SDIO_EXTENDED_IO_BLOCK_SIZE) != 0)
        {
            numBlocks++;
        }

        // write, block mode, address starts at 0, fixed address
        argument =  BUILD_IO_RW_EXTENDED_ARG(SD_IO_OP_WRITE, 
                                             SD_IO_BLOCK_MODE, 
                                             FW_DNLD_FUNC,     //Function Number
                                             SDIO_IO_PORT , 
                                             SD_IO_FIXED_ADDRESS, 
                                             numBlocks);
        transferClass = SD_WRITE;
        status = SDSynchronousBusRequest(Adapter->hDevice, 
                                         SD_CMD_IO_RW_EXTENDED,
                                         argument,
                                         transferClass, 
                                         ResponseR5,
                                         &response, 
                                         numBlocks,
                                         SDIO_EXTENDED_IO_BLOCK_SIZE, 
                                         (PUCHAR)&DownloadPkt,
                                         0); 
        if (!SD_API_SUCCESS(status))
        {
            DBGPRINT(DBG_LOAD,(L"Downloading FW died on block %d\n\r",blockCount));
#ifdef EXTERNAL_FW_IMAGE
            Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#endif
            return FW_STATUS_UNINITIALIZED; 
        }
        //DBGPRINT(DBG_LOAD, (L"FW Download block #%d\n\r",blockCount));
        sizeSend += sizeBlock;
    } 
 
    while (1)
    {
        // read card status register (function 1, addr 20)
        status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                            SD_IO_READ ,
                                            FW_DNLD_FUNC,
                                            0x10,
                                            FALSE,
                                            ucLen,
                                            2);
        if (!SD_API_SUCCESS(status))
        {           
#ifdef EXTERNAL_FW_IMAGE
            Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#endif
            return FW_STATUS_READ_FAILED; 
        }

        if (*((PUSHORT)(ucLen)) == 0x10)
        {
            //DBGPRINT(DBG_LOAD,(L"Download helper size == %x !!\n\r", *((PUSHORT)(ucLen))));
            break;
        }
        //DBGPRINT(DBG_LOAD,(L"Download helper size == %x !!\n\r", *((PUSHORT)(ucLen))));
    }
    DBGPRINT(DBG_LOAD,(L"Finished to download Helper !!\n\r"));
    //Download helper file-- 

#ifdef EXTERNAL_FW_IMAGE
    Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#ifdef MFG_MERGE
    if (Adapter->UseMfgFw != 1)
        strcpy((PUCHAR)&name[0],"sd8688.bin");
    else
    {
        strcpy((PUCHAR)&name[0],"sd8688_mfg.bin");
        DBGPRINT(DBG_LOAD, (L"About to download MFG firmware !!\n\r"));
    }
#endif
    //Read in firmware image
    if ((status = Fopr_ReadImage(Adapter, (PUCHAR)&name[0], &flen, &vaddr)) 
                  != MRVL_STATUS_SUCCESS)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to READ the firmware\n\r")); 
        return FW_STATUS_READ_FAILED; 
    }    
    sizeOfFirmware = flen;
#else 
    sizeOfFirmware = sizeof(fmimage);
#endif

    blockCount = 0;
    sizeSend = 0;
    
    DBGPRINT(DBG_LOAD|DBG_WARNING,
             (L"INIT - Firmware download start, FW size = %d bytes, block size=%d bytes\n\r", 
             sizeOfFirmware, SDIO_FW_DOWNLOAD_BLOCK_SIZE));
   
    startFirmware = FALSE;
    exitLoop = FALSE;
    while (!exitLoop)
    {
        blockCount++;

        if (TRUE != startFirmware)
        {
            status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                                SD_IO_READ ,
                                                FW_DNLD_FUNC,
                                                0x10,
                                                FALSE,
                                                ucLen,
                                                2);
//            DBGPRINT(DBG_LOAD|DBG_HELP, (L"Download firmware size == %x !!\n\r",
//                                         *((PUSHORT)(ucLen))));

            if (!SD_API_SUCCESS(status))
            {        
#ifdef EXTERNAL_FW_IMAGE
                Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#endif
                return FW_STATUS_READ_FAILED; 
            }

            if (*((PUSHORT)(ucLen)) == 0)
            {
                DBGPRINT(DBG_LOAD|DBG_HELP, (L"End of download firmware!!\n\r"));
                break;
            }

            if ((*((PUSHORT)(ucLen)) & 1) == 1)
            {
                 DBGPRINT(DBG_LOAD|DBG_ERROR, (L"download firmware with CRC error!!\n\r"));
            }
            else
            {
                 //DBGPRINT(DBG_LOAD|DBG_HELP, (L"download firmware module success!!\n\r"));
                 // regular download
                 sizeBlock = *((PUSHORT)(ucLen));
            }

            // last pkt is not necessarily full pkt
            if ((sizeSend + sizeBlock) >= sizeOfFirmware)
            {
                sizeBlock = sizeOfFirmware - sizeSend;
                startFirmware = TRUE;
                exitLoop = TRUE;
            }
             
#ifdef EXTERNAL_FW_IMAGE
            MrvlMoveMemory(DownloadPkt.Buf, (PUCHAR)(vaddr+sizeSend), sizeBlock);
#else
            // Move the data from firmware buf to the command header
            MrvlMoveMemory(DownloadPkt.Buf, &fmimage[sizeSend], sizeBlock);
#endif
        } 

        DownloadPkt.Len = sizeBlock;
        
        // Len field ONLY accounts for the buffer size
        numBlocks = (((DownloadPkt.Len + 127) / 128) * 128) / 
                            SDIO_EXTENDED_IO_BLOCK_SIZE;

        if (((((DownloadPkt.Len + 127)/128)*128) % SDIO_EXTENDED_IO_BLOCK_SIZE) != 0)
        {
            numBlocks++;
        }

        //DBGPRINT(DBG_LOAD|DBG_HELP, (L"Block Needed = %d\n\r", numBlocks));

        // write, block mode, address starts at 0, fixed address
        argument = BUILD_IO_RW_EXTENDED_ARG(SD_IO_OP_WRITE, 
                                            SD_IO_BLOCK_MODE, 
                                            FW_DNLD_FUNC,      //Function Number
                                            SDIO_IO_PORT , 
                                            SD_IO_FIXED_ADDRESS, 
                                            numBlocks);
        transferClass = SD_WRITE;

        status = SDSynchronousBusRequest(Adapter->hDevice, 
                                         SD_CMD_IO_RW_EXTENDED,
                                         argument,
                                         transferClass, 
                                         ResponseR5,
                                         &response, 
                                         numBlocks,
                                         SDIO_EXTENDED_IO_BLOCK_SIZE, 
                                         (PUCHAR)&DownloadPkt.Buf,
                                         0); 
        if (!SD_API_SUCCESS(status))
        {
            DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Downloading FW died on block %d\n\r", blockCount));
#ifdef EXTERNAL_FW_IMAGE
            Fopr_DeallocatedFwImageBuffer(Adapter, flen, vaddr);
#endif
            return FW_STATUS_UNINITIALIZED; 
        }

        //DBGPRINT(DBG_LOAD|DBG_HELP, (L"FW Download block #%d\n\r", blockCount));
            
        sizeSend += sizeBlock;

        loopCount = PKT_WAIT_TIME;
        while (loopCount != 0)
        {
            // need to check if FW is ready for download

            // Read card status register (function 1, addr 20)
            status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                                SD_IO_READ ,
                                                FW_DNLD_FUNC,
                                                HCR_HOST_CARD_STATUS_REGISTER,
                                                FALSE,
                                                &ucCardStatus,
                                                sizeof(ucCardStatus));
            if (!SD_API_SUCCESS(status))
            {
                DBGPRINT(DBG_LOAD, (L"Read card status register failed !\n\r")); 
#ifdef EXTERNAL_FW_IMAGE
                Fopr_DeallocatedFwImageBuffer(Adapter, flen,vaddr); 
#endif
                return FW_STATUS_READ_FAILED; 
            }

            if ((!(ucCardStatus & SDIO_IO_READY)) ||  
                (!(ucCardStatus & SDIO_DOWNLOAD_CARD_READY)))
            {
                // Not ready for packet download
                loopCount--;
                Sleep(1);
            }
            else
            {
                break;
            }
        }

        if (loopCount == 0)
        {
            DBGPRINT(DBG_LOAD, (L"Downloading FW died on block %d\n\r", blockCount));
#ifdef EXTERNAL_FW_IMAGE
            Fopr_DeallocatedFwImageBuffer(Adapter, flen,vaddr); 
#endif
            return FW_STATUS_UNINITIALIZED; 
        }
        //DBGPRINT(DBG_LOAD, (L"Downloading FW loop-back \n\r"));
    }

    loopCount = PKT_WAIT_TIME;
    while (loopCount != 0)
    {
        if (sdio_IsFirmwareLoaded(Adapter) != FW_STATUS_INITIALIZED)
        {        
            // FW not ready    
            loopCount--;
            Sleep(1);
        }
        else
        {
            // FW ready!
            DBGPRINT(DBG_LOAD,(L"FW started SUCCESSFULLY !\n\r"));
#ifdef EXTERNAL_FW_IMAGE
            Fopr_DeallocatedFwImageBuffer(Adapter, flen,vaddr); 
#endif
            return FW_STATUS_INITIALIZED;
        }
    } 

#ifdef EXTERNAL_FW_IMAGE
    Fopr_DeallocatedFwImageBuffer(Adapter, flen,vaddr); 
#endif 

    DBGPRINT(DBG_LOAD|DBG_HELP, (L"FW did NOT start successfully !\n\r"));
    return FW_STATUS_UNINITIALIZED;
}


SD_API_STATUS SDIODownloadPkt(PMRVDRV_ADAPTER Adapter,
                              UCHAR Function,
                              PUCHAR pDownloadPkt,
                              ULONG DownloadLen)
{
    SD_API_STATUS           status;          // intermediate status
    SD_TRANSFER_CLASS       transferClass;   // general transfer class
    DWORD                   argument;        // argument
    ULONG                   numBlocks;       // number of blocks
    SD_COMMAND_RESPONSE     response;        // IO response status

    DBGPRINT(DBG_TX,(L"++ SDIODownloadPkt: Function %d ++\n\r", Function));

    // Calculate block number required for transmit
    numBlocks = DownloadLen / SDIO_EXTENDED_IO_BLOCK_SIZE;
    if ((DownloadLen % SDIO_EXTENDED_IO_BLOCK_SIZE) != 0)
    {
        numBlocks++;
    } 

    // write, block mode, address starts at 0, fixed address
    argument =  BUILD_IO_RW_EXTENDED_ARG(SD_IO_OP_WRITE, 
                                         SD_IO_BLOCK_MODE, 
                                         Function,
                                         SDIO_IO_PORT , 
                                         SD_IO_FIXED_ADDRESS, 
                                         numBlocks);
    transferClass = SD_WRITE;

    status = SDSynchronousBusRequest(Adapter->hDevice, 
                                     SD_CMD_IO_RW_EXTENDED,
                                     argument,
                                     transferClass, 
                                     ResponseR5,
                                     &response, 
                                     numBlocks,
                                     SDIO_EXTENDED_IO_BLOCK_SIZE, 
                                     (PUCHAR)pDownloadPkt,
                                     0); 
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ERROR, (L"SDIODownloadPkt(): Unable to download packet to FW !\n\r"));
        return status;
    }

    return status;
}


VOID sdio_EnableInterrupt(PMRVDRV_ADAPTER Adapter)
{
#if 0
    UCHAR ucInterrupt;
    SD_API_STATUS status;

    ucInterrupt = 0x3;
    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_WRITE,          
                                        BT_FUNC,     
                                        HCR_HOST_INT_MASK_REGISTER,
                                        FALSE,
                                        &ucInterrupt,
                                        sizeof(ucInterrupt)); 
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ERROR, (L"Error: *** Enable Interrupt ***\n\n"));
    }
#endif

    return;
}


VOID sdio_DisableInterrupt(PMRVDRV_ADAPTER Adapter)
{
#if 0
    UCHAR ucInterrupt;
    SD_API_STATUS status;

    ucInterrupt = 0x0;
    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_WRITE,          
                                        BT_FUNC,     
                                        HCR_HOST_INT_MASK_REGISTER,
                                        FALSE,
                                        &ucInterrupt,
                                        sizeof(ucInterrupt)); 
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ERROR,(L"Error: *** Disable Interrupt ***\n\n"));
    }
#endif

    return;
}


MRVL_STATUS SDIOInitialization(PMRVDRV_ADAPTER pAdapter, UCHAR Function)
{
    SDCARD_CLIENT_REGISTRATION_INFO clientInfo;     // client registration
    SD_API_STATUS                   sdStatus;       // SD Status
#ifdef MRVL_BT_DRV_ONLY
    SD_IO_FUNCTION_ENABLE_INFO      functionEnable; // enable sd card function
#endif
    SD_CARD_INTERFACE               cardInterface;  // card interface information
    SDIO_CARD_INFO                  sdioInfo;       // sdio info
    DWORD                           blockLength;    // block length
    UCHAR                           regValue;       // reg value
    
    DBGPRINT(DBG_LOAD|DBG_HELP, (L"++ SDIOInitialization ++\n\r"));
    DBGPRINT(DBG_LOAD|DBG_HELP, (L"Function Number %d\n\r", Function));

    if (pAdapter->hDevice != NULL)
    {
        memset(&clientInfo, 0, sizeof(clientInfo));

        // set client options and register as a client device
        _tcscpy(clientInfo.ClientName, TEXT("Marvell SDIO WLAN/BT Card"));

        // set the event callback
        clientInfo.pSlotEventCallBack = SDSlotEventCallBack;

        sdStatus = SDRegisterClient(pAdapter->hDevice, pAdapter, &clientInfo);
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, 
                    (L"Failed to register client ! 0x%08X\n\r", sdStatus));
            return MRVL_STATUS_FAILURE;
        }

#ifdef MRVL_BT_DRV_ONLY
        // set up the function enable struct
        // TODO use the appropriate retry and interval count for the function
        functionEnable.Interval = 500;
        functionEnable.ReadyRetryCount = 3;
    
        DBGPRINT(DBG_LOAD|DBG_HELP, (L"Enabling SD Card ... \n\r"));

        // turn off SD function
        sdStatus = SDSetCardFeature(pAdapter->hDevice,
                                    SD_IO_FUNCTION_DISABLE,
                                    &functionEnable,
                                    sizeof(functionEnable));   
        if (!SD_API_SUCCESS(sdStatus))
        {
             DBGPRINT(DBG_ERROR, (L"Failed to disable Function: 0x%08X\n\r", sdStatus));
             return MRVL_STATUS_FAILURE;
        }

        Sleep(500);

        functionEnable.Interval = 500;
        functionEnable.ReadyRetryCount = 3;

        // Enable SD function
        sdStatus = SDSetCardFeature(pAdapter->hDevice,
                                    SD_IO_FUNCTION_ENABLE,
                                    &functionEnable,
                                    sizeof(functionEnable));
        if (!SD_API_SUCCESS(sdStatus))
        {
             DBGPRINT(DBG_ERROR, (L"Failed to enable Function: 0x%08X\n\r", sdStatus));
             return MRVL_STATUS_FAILURE;
        }
        DBGPRINT(DBG_LOAD, (L"Enable Function OK\n\r", sdStatus));
#endif

        // query the card interface
        sdStatus = SDCardInfoQuery(pAdapter->hDevice,
                                   SD_INFO_CARD_INTERFACE,
                                   &cardInterface,
                                   sizeof(cardInterface));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, (L"Failed to query interface: 0x%08X\n\r", sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        if (cardInterface.ClockRate == 0)
        {
            DBGPRINT(DBG_ERROR, (L"Device interface rate is zero !\n\r"));
            return MRVL_STATUS_FAILURE;
        }

        DBGPRINT(DBG_LOAD, (L"[SDIO Quer]: Interface Clock : %d Hz\n\r",
                            cardInterface.ClockRate));
    
        if (cardInterface.InterfaceMode == SD_INTERFACE_SD_MMC_1BIT)
        {
            DBGPRINT(DBG_LOAD, (L"[SDIO Init 1]: Interface is 1 Bit mode\n\r"));
        }
        else if (cardInterface.InterfaceMode == SD_INTERFACE_SD_4BIT)
        {
            DBGPRINT(DBG_LOAD, (L"[SDIO Init 1]: Interface is 4 Bit mode\n\r"));
        }
        else
        {
            DBGPRINT(DBG_ERROR, (L"[SDIO Init 1]: Unknown interface mode! %d \n\r",
                                 cardInterface.InterfaceMode));
            return MRVL_STATUS_FAILURE;
        }
         
        pAdapter->SetSD4BIT = 1;        /* 0:1-bit mode, 1:4-bit mode */
        if (pAdapter->SetSD4BIT == 1)
        {
            DBGPRINT(DBG_LOAD, (L"Set Interface to 4 Bit mode\n\r"));
            cardInterface.InterfaceMode = SD_INTERFACE_SD_4BIT;
        }
        else
        {
            DBGPRINT(DBG_LOAD, (L"Set Interface to 1 Bit mode\n\r"));
            cardInterface.InterfaceMode = SD_INTERFACE_SD_MMC_1BIT;
        }

        DBGPRINT(DBG_LOAD, (L"[SDIO Init 1]: Set the SDIO clock rate and bit mode\n\r")); 
        // Set clock rate
        cardInterface.ClockRate = 20000000;  //25MHz
        sdStatus = SDSetCardFeature(pAdapter->hDevice,
                                    SD_SET_CARD_INTERFACE,
                                    &cardInterface,
                                    sizeof(cardInterface));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, 
                     (L"Set SD interface failed! 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        } 

        DBGPRINT(DBG_LOAD, (L"[SDIO Init 2]: Query the SDIO Interface information\n\r")); 
        sdStatus = SDCardInfoQuery(pAdapter->hDevice,
                                   SD_INFO_CARD_INTERFACE,
                                   &cardInterface,
                                   sizeof(cardInterface));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR,
                     (L"Failed to query interface info ! 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        if (cardInterface.ClockRate == 0)
        {
            DBGPRINT(DBG_ERROR, (L"Interface clock rate is zero !\n\r"));
            return MRVL_STATUS_FAILURE;
        }

        DBGPRINT(DBG_LOAD, (L"[SDIO Init 2]: Interface Clock : %d Hz\n\r",
                            cardInterface.ClockRate));

        if (cardInterface.InterfaceMode == SD_INTERFACE_SD_MMC_1BIT)
        {
            DBGPRINT(DBG_LOAD, (L"[SDIO Init 2]: Interface is 1 Bit mode\n\r"));
        }
        else if (cardInterface.InterfaceMode == SD_INTERFACE_SD_4BIT)
        {
            DBGPRINT(DBG_LOAD, (L"[SDIO Init 2]: Interface is 4 Bit mode\n\r"));
        }
        else
        {
            DBGPRINT(DBG_ERROR, (L"[SDIO Init 2]: Unknown interface mode ! %d\n\r",
                                 cardInterface.InterfaceMode));
            return MRVL_STATUS_FAILURE;
        }

        DBGPRINT(DBG_LOAD, (L"[SDIO Init 3]: Query the SDIO Card information\n\r"));
        // Query the SDIO information
        sdStatus = SDCardInfoQuery(pAdapter->hDevice,
                                   SD_INFO_SDIO,
                                   &sdioInfo,
                                   sizeof(sdioInfo));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, 
                     (L"Failed to query SDIO info ! 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        // Store SDIO function number of this device
        pAdapter->FunctionNumber = sdioInfo.FunctionNumber;
    
        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 3]: Function: %d\n\r", sdioInfo.FunctionNumber)); 
        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 3]: Device Code: %d\n\r", sdioInfo.DeviceCode)); 
        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 3]: CISPointer: 0x%08X\n\r", sdioInfo.CISPointer)); 
        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 3]: CSAPointer: 0x%08X\n\r", sdioInfo.CSAPointer)); 
        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 3]: CardCaps: 0x%02X\n\r", sdioInfo.CardCapability)); 

        // Check parameter of CIA
        if ((sdioInfo.CISPointer == 0) ||
            (sdioInfo.CardCapability == 0))
        {
            DBGPRINT(DBG_ERROR, (L"SDIO information is incorrect \n"));
            return MRVL_STATUS_FAILURE;
        }
          
        blockLength = SDIO_EXTENDED_IO_BLOCK_SIZE;

        // set the block length for this function
        sdStatus = SDSetCardFeature(pAdapter->hDevice,
                                    SD_IO_FUNCTION_SET_BLOCK_SIZE,
                                    &blockLength,
                                    sizeof(blockLength));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, 
                     (L"Failed to Set Block Length ! 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        DBGPRINT(DBG_LOAD|DBG_HELP, 
                 (L"[SDIO Init 4]: Block Size set to %d bytes \n\r",
                 blockLength));

        {
            SD_SET_FEATURE_TYPE nSdFeature;

            DBGPRINT(DBG_LOAD|DBG_HELP,
                     (L"SdioFastPath = %d\n\r", pAdapter->SdioFastPath));
        
            nSdFeature = (pAdapter->SdioFastPath == 1 ?
                          SD_FAST_PATH_ENABLE : SD_FAST_PATH_DISABLE);
            sdStatus = SDSetCardFeature(pAdapter->hDevice,
                                        nSdFeature,
                                        NULL,
                                        0);
            if (!SD_API_SUCCESS(sdStatus))
            {
                DBGPRINT(DBG_LOAD|DBG_ERROR,
                         (L"Fail to %s SDIO FAST PATH\n\r",
                         (nSdFeature == SD_FAST_PATH_ENABLE ? L"enable" : L"disable")));
                return MRVL_STATUS_FAILURE;
            }

            DBGPRINT(DBG_LOAD|DBG_HELP,
                     (L"[SDIO Init 5]: SDIO FAST PATH is %s\n\r",
                     (nSdFeature == SD_FAST_PATH_ENABLE ? L"enabled" : L"disabled")));
        }

#ifndef MRVL_WINCE50
        regValue = 3;
        // enable Client UpLdCardRdy interrup
        sdStatus = If_WriteRegister(pAdapter,
                                    //SD_IO_WRITE,          
                                    Function,     
                                    HCR_HOST_INT_MASK_REGISTER,
                                    FALSE,
                                    &regValue,
                                    1);   
        if (!SD_API_SUCCESS(sdStatus))
        {
             DBGPRINT(DBG_ERROR, 
                      (L"Failed to enable UpLdCardRdy interrupt: 0x%08X\n\r",
                      sdStatus));
             return MRVL_STATUS_FAILURE;
        } 
#endif 
        
       sdStatus = If_ReadRegister(pAdapter,
                                  //SD_IO_READ ,
                                  FN0,
                                  HCR_SDIO_BUS_INTERFACE_CONTROL , 
                                  FALSE,
                                  &regValue,
                                  sizeof(regValue));
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR,
                     (L"Failed to read Bus Interface Control Register: 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        if (cardInterface.InterfaceMode == SD_INTERFACE_SD_4BIT)
        {
            regValue = regValue | 0xA2;
        }
        else
        {
            regValue = regValue | 0xA0;
        }

        sdStatus = If_WriteRegister(pAdapter,
                                    //SD_IO_WRITE,          
                                    FN0,     
                                    HCR_SDIO_BUS_INTERFACE_CONTROL,
                                    FALSE,
                                    &regValue,
                                    1);   
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR, 
                     (L"Failed to enable Bus Interface Control Register: 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        // set up for write to clear
        // Set to read to clear mode for interrupt
        regValue = 3;
        sdStatus = If_WriteRegister(pAdapter,
                                    //SD_IO_WRITE,          
                                    Function,     
                                    HCR_HOST_INT_STATUS_RSR_REGISTER,
                                    FALSE,
                                    &regValue,
                                    1);   
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR,
                     (L"Failed to program Interrupt Write To Clear: 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        // Connect interrupt callback routine to SD bus driver
        sdStatus = SDIOConnectInterrupt(pAdapter->hDevice,
                                       (PSD_INTERRUPT_CALLBACK)SDInterruptCallback);    
        if (!SD_API_SUCCESS(sdStatus))
        {
            DBGPRINT(DBG_ERROR,
                     (L"Failed to connect interrupt: 0x%08X\n\r",
                     sdStatus));
            return MRVL_STATUS_FAILURE;
        }

        DBGPRINT(DBG_LOAD|DBG_HELP, (L"[Marvell BT] *** Card Ready ***\n\r"));
    }
    else    // (pAdapter->hDevice == NULL)
    {   
        DEBUG_ASSERT(FALSE);
    }

    return MRVL_STATUS_SUCCESS;
}

MRVL_STATUS sdio_Initialization(PMRVDRV_ADAPTER Adapter, UCHAR Function)
{
    if (SDIOInitialization(Adapter, Function) != MRVL_STATUS_SUCCESS)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"*** SDIOInitialization FAILED! ***\n\r"));
        return MRVL_STATUS_FAILURE;
    }
    return MRVL_STATUS_SUCCESS;
}

IF_API_STATUS sdio_ReadScratch(PMRVDRV_ADAPTER Adapter, UCHAR func, USHORT *pLength)
{
    SD_API_STATUS status;
    USHORT usLen0 = 0;
    USHORT usLen1 = 0;

#if 1  // Both way to read scratch register should be OK
    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_READ ,
                                        func,
                                        CARD_OCR_0_REG, 
                                        FALSE,
                                        (UCHAR *)&usLen0,
                                        sizeof(usLen0));
    if (!SD_API_SUCCESS(status))
    {
        return IF_FAIL;
    }

    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_READ ,
                                        func,
                                        CARD_OCR_1_REG, 
                                        FALSE,
                                        (UCHAR *)&usLen1,
                                        sizeof(usLen1));
    if (!SD_API_SUCCESS(status))
    {
        return IF_FAIL;
    }

    *pLength = (USHORT)((usLen1 << 8) | usLen0);
#else
    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_READ ,
                                        func,
                                        LENGTH_SCRATCH_REGISTER, 
                                        FALSE,
                                        (UCHAR *)&usLen0,
                                        sizeof(usLen0));
    if (!SD_API_SUCCESS(status))
    {
        return IF_FAIL;
    }

    *pLength = usLen0;
#endif

    return IF_SUCCESS;
}


IF_API_STATUS sdio_GetDataBlock(PMRVDRV_ADAPTER Adapter,
                                USHORT usLength,
                                UCHAR *pPacket)
{
    ULONG               numBlocks;
    DWORD               argument;
    SD_TRANSFER_CLASS   transferClass;
    SD_API_STATUS       status;
    SD_COMMAND_RESPONSE response;
    
    // calculate the number of blocks
    numBlocks = usLength / SDIO_EXTENDED_IO_BLOCK_SIZE;
      
    if ((usLength % SDIO_EXTENDED_IO_BLOCK_SIZE) != 0)
    {
        numBlocks++;
    }                      
           
    //IMPORTANT: WinCE 4.2 needs this block        
#ifndef MRVL_WINCE50
    if ((numBlocks % 2) != 0)
    {
        numBlocks++;
    } 
#endif
                        
    // read, block mode, address at 0, incrementing address
    argument =  BUILD_IO_RW_EXTENDED_ARG(SD_IO_OP_READ, 
                                         SD_IO_BLOCK_MODE, 
                                         BT_FUNC,     //Function Number
                                         SDIO_IO_PORT, 
                                         SD_IO_FIXED_ADDRESS, 
                                         numBlocks);
    transferClass = SD_READ;
    status = SDSynchronousBusRequest(Adapter->hDevice, 
                                     SD_CMD_IO_RW_EXTENDED,
                                     argument,
                                     transferClass, 
                                     ResponseR5,
                                     &response, 
                                     numBlocks,
                                     SDIO_EXTENDED_IO_BLOCK_SIZE, 
                                     (PUCHAR)pPacket,
                                     0); 
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ERROR, (L"Unable to read packet ! 0x%x, %d, %d\r\n",
                             status, numBlocks, usLength));
        return IF_FAIL;
    } 

    return IF_SUCCESS;
}

IF_API_STATUS sdio_ReadRegistersDirect(PMRVDRV_ADAPTER Adapter, 
                                       UCHAR nFunc,
                                       DWORD dwAddr,
                                       UINT bReadAfterWrite,
                                       UCHAR *pBuf,
                                       ULONG nBufLen)
{
    SD_API_STATUS status;

    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_READ ,
                                        nFunc,
                                        dwAddr, 
                                        bReadAfterWrite,
                                        pBuf,
                                        nBufLen);
    if (!SD_API_SUCCESS(status))
        return IF_FAIL;

    return IF_SUCCESS;
}

IF_API_STATUS sdio_WriteRegistersDirect(PMRVDRV_ADAPTER Adapter,
                                        UCHAR nFunc,
                                        DWORD dwAddr,
                                        UINT bReadAfterWrite,
                                        UCHAR *pBuf,
                                        ULONG nBufLen)
{
    SD_API_STATUS status;

    status = SDReadWriteRegistersDirect(Adapter->hDevice,
                                        SD_IO_WRITE ,
                                        nFunc,
                                        dwAddr, 
                                        bReadAfterWrite,
                                        pBuf,
                                        nBufLen);

    if (!SD_API_SUCCESS(status))
        return IF_FAIL;

    return IF_SUCCESS;
} 

#ifdef UNDER_WINCE500
/*
 * Under WinCE 5.0, system doesn't provide such SD_CARD_FORCE_RESET... index value.
 */
IF_API_STATUS sdio_ReInitCard(PMRVDRV_ADAPTER Adapter)
{
    return IF_SUCCESS;
}
#else
IF_API_STATUS sdio_ReInitCard(PMRVDRV_ADAPTER Adapter)
{
    SD_API_STATUS Status;  

    Status = SDSetCardFeature(Adapter->hDevice,
                              SD_CARD_FORCE_RESET,
                              NULL,
                              0);
    if (!SD_API_SUCCESS(Status))
    {  
        DBGPRINT(DBG_ERROR, (L"SD: Fail to force card reset\n\r", Status));
        return IF_FAIL;
    }

    Status = SDSetCardFeature(Adapter->hDevice,
                              SD_CARD_DESELECT_REQUEST,
                              NULL,
                              0);
    if (!SD_API_SUCCESS(Status))
    {  
        DBGPRINT(DBG_ERROR, (L"SD: Fail to deselect card\n\r", Status));
        return IF_FAIL;
    }

    Status = SDSetCardFeature(Adapter->hDevice,
                                SD_CARD_SELECT_REQUEST,
                                NULL,
                                0);
    if (!SD_API_SUCCESS(Status))
    {  
        DBGPRINT(DBG_ERROR, (L"SD: Fail to select card\n\r", Status)); 
        return IF_FAIL;
    }

    return IF_SUCCESS;
}
#endif // WINCE500 

IF_API_STATUS sdio_PowerUpDevice(PMRVDRV_ADAPTER Adapter, UCHAR function)
{
    UCHAR regValue;
    IF_API_STATUS sdStatus; 

    DBGPRINT(DBG_PS, (L"Setup PowerUP bit\r\n"));

    regValue = 0x2;  //Configuration: Host Offset: 0x03 Write Bit1=1
    sdStatus = If_WriteRegister(Adapter,
                                //SD_IO_WRITE,          
                                function,     
                                HCR_HOST_CONFIGURATION_REGISTER,  //0x03 
                                FALSE,
                                &regValue,   // reg
                                1);   
    
    if (!SD_API_SUCCESS(sdStatus))
    {
        DBGPRINT(DBG_ERROR, (L"Set PowerUp bit Fail: %x\r\n", sdStatus));
        return IF_FAIL;
    }  
    
    return IF_SUCCESS;
}

IF_API_STATUS sdio_ClearPowerUpBit(PMRVDRV_ADAPTER Adapter, UCHAR function)
{
    UCHAR   regValue;
    IF_API_STATUS sdStatus = IF_SUCCESS; 

    regValue = 0x0;  //Configuration: Host Offset: 0x03
    sdStatus = If_WriteRegister(Adapter,
                                //SD_IO_WRITE,          
                                function,     
                                HCR_HOST_CONFIGURATION_REGISTER,  //0x03 
                                FALSE,
                                &regValue,   // reg
                                1);   
    
    if (!SD_API_SUCCESS(sdStatus))
    {
        DBGPRINT(DBG_ERROR, (L"Clear PowerUp bit Fail: %x\r\n", sdStatus));
        return IF_FAIL;
    }  

    DBGPRINT(DBG_PS|DBG_WARNING, (L"Clear PowerUP bit\r\n"));

    return IF_SUCCESS;
}


#endif // IF_SDIO
